#include "../../core/src/log.h"
#include "setting.h"
#include "eventfactory.h"
#include "eventqueue.h"
#include "eventdispatcher.h"
#include "../../core/src/asyncservice.h"
#include "../../core/src/databaseservice.h"
#include "../../core/src/netservice.h"
#include "../../core/src/timerservice.h"
#include "databaseassist.h"
#include "networkassist.h"
#include "server.h"

Server::Server() :
    m_memoryAllocator(sizeof(char))
{

}

Server::~Server()
{

}

const Server::SettingPtr &Server::settings()
{
    return m_setting;
}

void *Server::allocMemory(size_t n)
{
    return m_memoryAllocator.ordered_malloc(n);
}

bool Server::freeMemory(void *p, size_t n)
{
    if(!m_memoryAllocator.is_from(p)){
        return false;
    }

    m_memoryAllocator.ordered_free(p, n);

    return true;
}

const Server::EventFactoryPtr &Server::eventFactory()
{
    return m_eventFactory;
}

const Server::EventQueuePtr &Server::eventQueue()
{
    return m_eventQueue;
}

const Server::EventDispatcherPtr &Server::eventDispatcher()
{
    return m_eventDispatcher;
}

const Server::TimerServicePtr &Server::timerService()
{
    return m_timerService;
}

const Server::NetServicePtr &Server::netService()
{
    return m_netService;
}

const Server::DatabaseServicePtr &Server::dbService()
{
    return m_dbService;
}

const Server::AsyncServicePtr &Server::asyncService()
{
    return m_service;
}

int Server::start(int argc, char **argv)
{
    if(!parseCommandLine(argc, argv)){
        return -1;
    }

    if(!initialize()){
        return -1;
    }

    return run();
}

bool Server::parseCommandLine(int argc, char **argv)
{
    Log::AutoLog al(__FUNCTION__, __FILE__, __LINE__);

    LOG_TRACE() << "Server start argument count: " << argc;

    for(int i = 0; i < argc; ++i){
        LOG_TRACE() << "Argument " << i + 1 << ": " << argv[i];
    }

    return true;
}

bool Server::initialize()
{
    Log::AutoLog al(__FUNCTION__, __FILE__, __LINE__);

    if(!initSettings()){
        LOG_ERROR() << "Initialize server setting.";
        return false;
    }

    if(!initEventComponents()){
        LOG_ERROR() << "Initialize server event components.";
        return false;
    }

    if(!initServices()){
        LOG_ERROR() << "Initialize server asynchronous services.";

        return false;
    }

    if(!initAssists()){
        LOG_ERROR() << "Initialize server assist classes.";

        return false;
    }

    if(!startNetwork()){
        LOG_ERROR() << "Network start failed!";

        return false;
    }

    if(!startThreads()){
        LOG_ERROR() << "All work threads start failed!";

        return false;
    }

    return true;
}

int Server::run()
{
    Log::AutoLog al(__FUNCTION__, __FILE__, __LINE__);

    return m_eventDispatcher->loop();
}

bool Server::initSettings()
{
    Log::AutoLog al(__FUNCTION__, __FILE__, __LINE__);

    m_setting = SettingPtr(new Setting());

    if(!m_setting){
        LOG_ERROR() << "Create setting instance: "
                    << strerror(boost::system::errc::not_enough_memory);
        return false;
    }

    if(!m_setting->init("")){
        LOG_ERROR() << "Initialize settings.";
        return false;
    }

    return true;
}

bool Server::initEventComponents()
{
    Log::AutoLog al(__FUNCTION__, __FILE__, __LINE__);

    m_eventFactory = EventFactoryPtr(new EventFactory(*this));

    if(!m_eventFactory){
        LOG_ERROR() << "Create event factory: "
                    << strerror(boost::system::errc::not_enough_memory);

        return false;
    }

    m_eventQueue = EventQueuePtr(new EventQueue());

    if(!m_eventQueue){
        LOG_ERROR() << "Create event queue: "
                    << strerror(boost::system::errc::not_enough_memory);

        return false;
    }

    m_eventDispatcher = EventDispatcherPtr(new EventDispatcher(*this));

    if(!m_eventDispatcher){
        LOG_ERROR() << "Create event dispatcher: "
                    << strerror(boost::system::errc::not_enough_memory);

        return false;
    }

    return true;
}

bool Server::initServices()
{
    Log::AutoLog al(__FUNCTION__, __FILE__, __LINE__);

    int ret = 0;

    m_service = AsyncServicePtr(new AsyncService());

    if(!m_service){
        LOG_ERROR() << "Create asynchronous service: "
                    << strerror(boost::system::errc::not_enough_memory);

        return false;
    }

    m_dbService = DatabaseServicePtr(new DatabaseService(*m_service));

    if(!m_dbService){
        LOG_ERROR() << "Create asynchronous database service: "
                    << strerror(boost::system::errc::not_enough_memory);

        return false;
    }

    ret = m_dbService->init
            (
                m_setting->settingData< DatabaseSettingData >()->dbName,
                m_setting->settingData< DatabaseSettingData >()->dbHost,
                m_setting->settingData< DatabaseSettingData >()->dbUser,
                m_setting->settingData< DatabaseSettingData >()->dbPassword,
                m_setting->settingData< DatabaseSettingData >()->dbPort
            );

    if(ret){
        LOG_ERROR() << "Asynchronous database service initialize.";

        return false;
    }

    m_netService = NetServicePtr(new NetService(*m_service));

    if(!m_netService){
        LOG_ERROR() << "Create asynchronous network service: "
                    << strerror(boost::system::errc::not_enough_memory);

        return false;
    }

    ret = m_netService->initialize();

    if(ret){
        LOG_ERROR() << "Asynchronous net service initialize: "
                    << strerror(ret);
        return false;
    }

    m_timerService = TimerServicePtr(new TimerService(*m_service));

    if(!m_timerService){
        LOG_ERROR() << "Create asynchronous timer service: "
                    << strerror(boost::system::errc::not_enough_memory);

        return false;
    }

    return true;
}

bool Server::initAssists()
{
    Log::AutoLog al(__FUNCTION__, __FILE__, __LINE__);

    m_netAssist = NetworkAssistPtr(new NetworkAssist(*this));

    if(!m_netAssist){
        LOG_ERROR() << "Create network assist: "
                    << strerror(boost::system::errc::not_enough_memory);

        return false;
    }

    m_dbAssist = DatabaseAssistPtr(new DatabaseAssist(*this));

    if(!m_dbAssist){
        LOG_ERROR() << "Create database assist: "
                    << strerror(boost::system::errc::not_enough_memory);

        return false;
    }

    return true;
}

bool Server::startNetwork()
{
    Log::AutoLog al(__FUNCTION__, __FILE__, __LINE__);

    return true;
}

int Server::startThreads()
{
    Log::AutoLog al(__FUNCTION__, __FILE__, __LINE__);

    int ret = 0;

    for(unsigned short i = 0;
        i < m_setting->settingData< ServerSettingData >()->workThreadCount; ++i){

        if(m_threads.create_thread(boost::bind(&Server::routine, this))){
            LOG_TRACE() << "Create work thread " << i + 1 << " success.";
            ++ret;
        }else{
            LOG_WARNING() << "Create work thread " << i + 1 << " failed!";
        }
    }

    if(ret){
        LOG_TRACE() << "Server will work with " << ret << " count threads.";
    }

    return ret;
}

void Server::routine()
{
    Log::AutoLog al(__FUNCTION__, __FILE__, __LINE__);

    m_service->run();
}
